package com.eastedu.report.principal.assemble;

import cn.hutool.core.text.StrFormatter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.deepoove.poi.data.*;
import com.eastedu.common.enums.DateCodeType;
import com.eastedu.report.common.model.parameter.ReportGenerateParameter;
import com.eastedu.report.common.model.po.CommonDimensionEntity;
import com.eastedu.report.common.model.po.ResourceReceiveDetailUsagePO;
import com.eastedu.report.common.model.po.ResourceReceiveTotalUsagePO;
import com.eastedu.report.common.util.NumberFormatUtil;
import com.eastedu.report.common.util.SqlUtil;
import com.eastedu.report.mapper.ResourceReceiveDetailUsageMapper;
import com.eastedu.report.mapper.ResourceReceiveTotalUsageMapper;
import com.eastedu.report.principal.model.receive.ResourceReceiveDetailReportData;
import com.eastedu.report.principal.model.receive.ResourceReceiveDetailReportDataTotal;
import com.eastedu.utils.CollectionUtils;
import com.eastedu.utils.ContinuousElementsUtil;
import com.eastedu.utils.DateUtil;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 资源接收报告数据组装
 *
 * @author zhangzhishuai
 */
@Service
@RequiredArgsConstructor
public class ResourceReceiveReportDataAssembly implements ReportDataAssembly {

    public static final String HORIZONTAL_LINE = "-";
    private final ResourceReceiveDetailUsageMapper resourceReceiveDetailUsageMapper;
    private final ResourceReceiveTotalUsageMapper resourceReceiveTotalUsageMapper;


    @Override
    public Map<String, Object> assemble(ReportGenerateParameter parameter, List<String> schoolPeriods, List<String> subjects) {
        Map<String, Object> map = new HashMap<>(10);
        Map<DateCodeType, ResourceReceiveTotalUsagePO> dateCodeTypeAndTotalMap = getResourceReceiveTotalUsagePo(parameter);
        Map<DateCodeType, List<ResourceReceiveDetailUsagePO>> dateCodeTypeAndDetailMap = getResourceReceiveDetailUsagePoList(parameter);
        String term = DateUtil.convertTermToSchoolYear(String.valueOf(parameter.getCodeType().toTerm(parameter.getDateCode())));
        map.put("resource_receive_total_usage_desc_month", getDescWithMonth(dateCodeTypeAndTotalMap.get(DateCodeType.MONTH), dateCodeTypeAndDetailMap.get(DateCodeType.MONTH)));
        map.put("resource_receive_total_usage_desc_term", getDescWithTerm(term, dateCodeTypeAndTotalMap.get(DateCodeType.TERM), dateCodeTypeAndDetailMap.get(DateCodeType.TERM)));
        List<ResourceReceiveDetailUsagePO> receiveDetailUsagePoList = dateCodeTypeAndDetailMap.values()
                .stream().flatMap(Collection::stream).collect(Collectors.toList());
        map.put("resource_receive_detail_usage_table", assembleTableData(receiveDetailUsagePoList, term + "学期", dateCodeTypeAndTotalMap));
        return map;
    }

    private TableRenderData assembleTableData(List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList, String term, Map<DateCodeType, ResourceReceiveTotalUsagePO> dateCodeTypeAndDataMap) {
        Tables.TableBuilder tableBuilder = getTableBuilder(term, resourceReceiveDetailUsagePoList, dateCodeTypeAndDataMap);
        return tableBuilder.create();
    }

    private Tables.TableBuilder getTableBuilder(String term, List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList, Map<DateCodeType, ResourceReceiveTotalUsagePO> dateCodeTypeAndDataMap) {
        List<ResourceReceiveDetailReportData> resourceReceiveDetailReportData = getResourceReceiveDetailReportData(resourceReceiveDetailUsagePoList);
        List<List<String>> rowsHeader = getHeaders(term);
        List<List<String>> rowsBody = getBody(resourceReceiveDetailReportData);
        List<List<String>> rowsTail = getTailStruct();

        List<List<String>> all = Lists.newArrayList();
        all.addAll(rowsHeader);
        all.addAll(rowsBody);
        all.addAll(rowsTail);
        MergeCellRule.MergeCellRuleBuilder builder = MergeCellRule.builder();
        ContinuousElementsUtil.calculateAndConvert(all,
                (ContinuousElementsUtil.ConvertConsumer) (start, end) ->
                        builder.map(MergeCellRule.Grid.of(start.getX(), start.getY()), MergeCellRule.Grid.of(end.getX(), end.getY())));

        Tables.TableBuilder tableBuilder = Tables.of().mergeRule(builder.build()).width(18, new double[]{1, 2.6, 1.8, 1.8, 1.8, 1.8, 1.8, 1.8, 1.8, 1.8});

        // 表头1
        for (List<String> headers : rowsHeader) {
            Rows.RowBuilder rowBuilder = Rows.of().bgColor("4e70ee").textBold().textColor("ffffff").textFontSize(8).center();
            for (String header : headers) {
                rowBuilder.addCell(Cells.create(header));
            }
            tableBuilder.addRow(rowBuilder.create());
        }

        // body
        for (ResourceReceiveDetailReportData data : resourceReceiveDetailReportData) {
            Rows.RowBuilder rowBuilder = getRowBuilder(data);
            tableBuilder.addRow(rowBuilder.create());
        }

        // tail
        List<List<String>> tailData = getTail(dateCodeTypeAndDataMap.get(DateCodeType.MONTH), dateCodeTypeAndDataMap.get(DateCodeType.TERM));
        for (List<String> tails : tailData) {
            Rows.RowBuilder rowBuilder = Rows.of().bgColor("4e70ee").textBold().textColor("ffffff").textFontSize(8).center();
            for (String tail : tails) {
                rowBuilder.addCell(Cells.create(tail));
            }
            tableBuilder.addRow(rowBuilder.create());
        }
        return tableBuilder;
    }

    private static Rows.RowBuilder getRowBuilder(ResourceReceiveDetailReportData data) {
        Rows.RowBuilder rowBuilder = Rows.of().textFontSize(8).center();
        rowBuilder.addCell(Cells.of(Texts.of(data.getTeachingActivity()).bold().create()).bgColor("c4ccea").create());
        rowBuilder.addCell(Cells.of(Texts.of(data.getResourceTypeName()).bold().create()).bgColor("c4ccea").create());
        rowBuilder.addCell(Cells.create(data.getMonthFollowedResourceReceiveNumber()));
        rowBuilder.addCell(Cells.create(data.getMonthFollowedResourceUseNumber()));
        rowBuilder.addCell(Cells.create(data.getMonthFollowedResourceUseRate()));
        rowBuilder.addCell(Cells.create(data.getMonthHistoryResourceUseNumber()));
        rowBuilder.addCell(Cells.create(data.getTermFollowedResourceReceiveNumber()));
        rowBuilder.addCell(Cells.create(data.getTermFollowedResourceUseNumber()));
        rowBuilder.addCell(Cells.create(data.getTermFollowedResourceUseRate()));
        rowBuilder.addCell(Cells.create(data.getTermHistoryResourceUseNumber()));
        return rowBuilder;
    }

    private static List<List<String>> getTailStruct() {
        List<List<String>> rowsTail = Lists.newArrayList();
        List<String> cells = Lists.newArrayList("合计", "合计",
                UUID.randomUUID().toString(), 
                UUID.randomUUID().toString(), 
                UUID.randomUUID().toString(), 
                UUID.randomUUID().toString(), 
                UUID.randomUUID().toString(), 
                UUID.randomUUID().toString(), 
                UUID.randomUUID().toString(), 
                UUID.randomUUID().toString());
        rowsTail.add(cells);
        return rowsTail;
    }

    private List<List<String>> getBody(List<ResourceReceiveDetailReportData> resourceReceiveDetailReportData) {
        return resourceReceiveDetailReportData.stream()
                .map(po -> Lists.newArrayList(po.getTeachingActivity(), po.getResourceTypeName(),
                        UUID.randomUUID().toString(),
                        UUID.randomUUID().toString(),
                        UUID.randomUUID().toString(),
                        UUID.randomUUID().toString(),
                        UUID.randomUUID().toString(),
                        UUID.randomUUID().toString(),
                        UUID.randomUUID().toString(),
                        UUID.randomUUID().toString()))
                .collect(Collectors.toList());
    }

    private static List<List<String>> getHeaders(String term) {
        List<String> firstHeaders = Lists.newArrayList("环节", "资源类型");
        List<String> groups = Lists.newArrayList("本月", term);
        List<String> cols = Lists.newArrayList("同步资源接收个数", "同步资源使用个数", "同步资源使用率", "历史资源使用个数");
        List<List<String>> rowsHeader = Lists.newArrayList();
        List<String> row1 = new ArrayList<>(firstHeaders);
        for (String group : groups) {
            for (String ignored : cols) {
                row1.add(group);
            }
        }
        List<String> row2 = new ArrayList<>(firstHeaders);
        for (String ignored : groups) {
            row2.addAll(cols);
        }
        rowsHeader.add(row1);
        rowsHeader.add(row2);
        return rowsHeader;
    }

    private List<List<String>> getTail(ResourceReceiveTotalUsagePO month, ResourceReceiveTotalUsagePO term) {
        if (Objects.isNull(month)) {
            month = new ResourceReceiveTotalUsagePO();
        }
        if (Objects.isNull(term)) {
            term = new ResourceReceiveTotalUsagePO();
        }
        ResourceReceiveDetailReportDataTotal detailReportDataTotal = ResourceReceiveDetailReportDataTotal.builder()
                .monthHistoryResourceUseTotalNumber(nullDefault(month.getHistoryResourceUseNumber()))
                .monthFollowedResourceReceiveTotalNumber(nullDefault(month.getFollowedResourceReceiveNumber()))
                .monthFollowedResourceUseTotalNumber(nullDefault(month.getFollowedResourceUseNumber()))
                .monthFollowedResourceTotalUseRate(NumberFormatUtil.formatPercent(month.getFollowedResourceUseRate(), HORIZONTAL_LINE))
                .termHistoryResourceUseTotalNumber(nullDefault(term.getHistoryResourceUseNumber()))
                .termFollowedResourceReceiveTotalNumber(nullDefault(term.getFollowedResourceReceiveNumber()))
                .termFollowedResourceUseTotalNumber(nullDefault(term.getFollowedResourceUseNumber()))
                .termFollowedResourceTotalUseRate(NumberFormatUtil.formatPercent(term.getFollowedResourceUseRate()))
                .build();
        List<List<String>> data = Lists.newArrayList();
        data.add(Lists.newArrayList("合计", "合计", detailReportDataTotal.getMonthFollowedResourceReceiveTotalNumber(),
                detailReportDataTotal.getMonthFollowedResourceUseTotalNumber(), detailReportDataTotal.getMonthFollowedResourceTotalUseRate(),
                detailReportDataTotal.getMonthHistoryResourceUseTotalNumber(), detailReportDataTotal.getTermFollowedResourceReceiveTotalNumber(),
                detailReportDataTotal.getTermFollowedResourceUseTotalNumber(), detailReportDataTotal.getTermFollowedResourceTotalUseRate(),
                detailReportDataTotal.getTermHistoryResourceUseTotalNumber()));
        return data;
    }


    private List<ResourceReceiveDetailReportData> getResourceReceiveDetailReportData(List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList) {
        List<ResourceReceiveDetailReportData> resourceReceiveDetailReportData = new ArrayList<>(10);

        Map<String, List<ResourceReceiveDetailUsagePO>> teachingActivityAndDataMap = getTeachingActivityAndDataMap(resourceReceiveDetailUsagePoList);

        teachingActivityAndDataMap.forEach((teachingActivity, detailUsages) -> {
            Map<DateCodeType, List<ResourceReceiveDetailUsagePO>> codeTypeAndUsages = detailUsages.stream()
                    .collect(Collectors.groupingBy(CommonDimensionEntity::getCodeType));
            List<ResourceReceiveDetailUsagePO> monthData = codeTypeAndUsages.getOrDefault(DateCodeType.MONTH, Collections.emptyList());
            List<ResourceReceiveDetailUsagePO> termData = codeTypeAndUsages.getOrDefault(DateCodeType.TERM, Collections.emptyList());
            Map<String, ResourceReceiveDetailUsagePO> resourceTypeNameAndMonthDataMap = monthData.stream()
                    .collect(Collectors.toMap(ResourceReceiveDetailUsagePO::getResourceTypeName, Function.identity(), (t1, t2) -> t1));
            List<ResourceReceiveDetailReportData> detailReportData = termData.stream()
                    .map(termItem -> getResourceReceiveDetailReportData(teachingActivity, termItem, resourceTypeNameAndMonthDataMap))
                    .collect(Collectors.toList());
            resourceReceiveDetailReportData.addAll(detailReportData);
        });
        Map<String, ResourceReceiveDetailReportData> dataMap = resourceReceiveDetailReportData.stream()
                .collect(Collectors.toMap(o -> o.getTeachingActivity() + HORIZONTAL_LINE + o.getResourceTypeName(), Function.identity(), (t1, t2) -> t1));
        List<String> names = Lists.newArrayList(
                "同步备课-备课直播/实录",
                "同步备课-备课课件",
                "同步备课-备课纪要",
                "同步教学-课堂实录",
                "同步教学-教学课件",
                "同步教学-教案",
                "同步教学-教学素材",
                "同步教学-作业单",
                "同步教研-教研直播/实录",
                "同步教研-教研课件",
                "同步教研-教研素材",
                "学生活动-活动直播/实录",
                "学生活动-活动方案",
                "学生活动-活动素材",
                "学生活动-学生范例",
                "学生活动-课外拓展",
                "同步测验-测验单",
                "同步测验-绝密试卷"
        );
        return names.stream().map(name -> dataMap.getOrDefault(name, defaultResourceReceiveDetailReportData(name)))
                .collect(Collectors.toList());
    }

    private Map<String, List<ResourceReceiveDetailUsagePO>> getTeachingActivityAndDataMap(List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList) {
        if (CollectionUtils.isEmpty(resourceReceiveDetailUsagePoList)) {
            return Collections.emptyMap();
        }
        return resourceReceiveDetailUsagePoList
                .stream().collect(Collectors.groupingBy(ResourceReceiveDetailUsagePO::getTeachingActivity));
    }

    private ResourceReceiveDetailReportData defaultResourceReceiveDetailReportData(String name) {
        String[] split = name.split(HORIZONTAL_LINE);
        return ResourceReceiveDetailReportData.builder()
                .teachingActivity(split[0])
                .resourceTypeName(split[1])
                .monthFollowedResourceReceiveNumber(HORIZONTAL_LINE)
                .monthFollowedResourceUseNumber(HORIZONTAL_LINE)
                .monthFollowedResourceUseRate(HORIZONTAL_LINE)
                .monthHistoryResourceUseNumber(HORIZONTAL_LINE)
                .termFollowedResourceReceiveNumber(HORIZONTAL_LINE)
                .termFollowedResourceUseNumber(HORIZONTAL_LINE)
                .termFollowedResourceUseRate(HORIZONTAL_LINE)
                .termHistoryResourceUseNumber(HORIZONTAL_LINE)
                .build();
    }

    private ResourceReceiveDetailReportData getResourceReceiveDetailReportData(String teachingActivity,
                                                                                      ResourceReceiveDetailUsagePO termItem,
                                                                                      Map<String, ResourceReceiveDetailUsagePO> resourceTypeNameAndMonthDataMap) {
        ResourceReceiveDetailReportData data = new ResourceReceiveDetailReportData();
        data.setTeachingActivity(teachingActivity);
        data.setResourceTypeName(termItem.getResourceTypeName());
        data.setTermFollowedResourceReceiveNumber(nullDefault(termItem.getFollowedResourceReceiveNumber()));
        data.setTermFollowedResourceUseNumber(nullDefault(termItem.getFollowedResourceUseNumber()));
        data.setTermFollowedResourceUseRate(NumberFormatUtil.formatPercent(termItem.getFollowedResourceUseRate()));
        data.setTermHistoryResourceUseNumber(nullDefault(termItem.getHistoryResourceUseNumber()));

        ResourceReceiveDetailUsagePO monthItem = resourceTypeNameAndMonthDataMap.getOrDefault(termItem.getResourceTypeName(), new ResourceReceiveDetailUsagePO());
        data.setMonthFollowedResourceReceiveNumber(nullDefault(monthItem.getFollowedResourceReceiveNumber()));
        data.setMonthFollowedResourceUseNumber(nullDefault(monthItem.getFollowedResourceUseNumber()));
        data.setMonthFollowedResourceUseRate(NumberFormatUtil.formatPercent(monthItem.getFollowedResourceUseRate(), HORIZONTAL_LINE));
        data.setMonthHistoryResourceUseNumber(nullDefault(monthItem.getHistoryResourceUseNumber()));
        return data;
    }

    private String nullDefault(Integer number) {
        return Objects.isNull(number) ? ResourceReceiveReportDataAssembly.HORIZONTAL_LINE : number.toString();
    }

    private static String getDescWithMonth(ResourceReceiveTotalUsagePO resourceReceiveTotalUsage, List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList) {
        String templateA = "本月，学校教师共计接收了{}个同步资源，使用了{}个同步资源，同步资源使用率为{}。";
        String templateB = "其中，同步的{}的资源使用率最高，为{}。";
        String templateC = "此外，学校教师暂未使用过历史资源。";
        String templateC1 = "此外，学校教师共计使用了{}个历史资源。";
        return getDesc(resourceReceiveTotalUsage, resourceReceiveDetailUsagePoList, templateA, templateB, templateC, templateC1);
    }

    private static String getDescWithTerm(String term, ResourceReceiveTotalUsagePO resourceReceiveTotalUsage, List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList) {
        String templateA = "截止本月，" + term + "这一学期学校教师累计接收了{}个同步资源，累计使用了{}个同步资源，同步资源使用率为{}。";
        String templateB = "其中，同步的{}资源使用率最高，为{}。";
        String templateC = "此外，这一学期学校教师暂未使用过历史资源。";
        String templateC1 = "此外，这一学期学校教师共计使用了{}个历史资源。";
        return getDesc(resourceReceiveTotalUsage, resourceReceiveDetailUsagePoList, templateA, templateB, templateC, templateC1);
    }

    private static String getDesc(ResourceReceiveTotalUsagePO resourceReceiveTotalUsage, List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList, String templateA, String templateB, String templateC, String templateC1) {
        String desc;
        if (Objects.isNull(resourceReceiveTotalUsage)) {
            desc = StrFormatter.format(templateA, 0, 0, "0.0%");
            return desc;
        }
        desc = StrFormatter.format(templateA, resourceReceiveTotalUsage.getFollowedResourceReceiveNumber(),
                                   resourceReceiveTotalUsage.getFollowedResourceUseNumber(),
                                   NumberFormatUtil.formatPercent(resourceReceiveTotalUsage.getFollowedResourceUseRate()));
        String zero = "0";
        if (!CollectionUtils.isEmpty(resourceReceiveDetailUsagePoList) && !new Double(zero).equals(resourceReceiveTotalUsage.getFollowedResourceUseRate())) {
            Double maxUtilizationRate = resourceReceiveDetailUsagePoList.stream()
                    .max(Comparator.comparingDouble(ResourceReceiveDetailUsagePO::getFollowedResourceUseRate))
                    .map(ResourceReceiveDetailUsagePO::getFollowedResourceUseRate).orElse(new Double(zero));
            String joinResourceTypeName = getJoinResourceTypeName(resourceReceiveDetailUsagePoList, maxUtilizationRate);
            String formatB = StrFormatter.format(templateB, joinResourceTypeName, NumberFormatUtil.formatPercent(maxUtilizationRate));
            desc += formatB;
        }
        if (new Integer(zero).equals(resourceReceiveTotalUsage.getHistoryResourceUseNumber())) {
            desc += templateC;
        } else {
            String formatC1 = StrFormatter.format(templateC1, resourceReceiveTotalUsage.getHistoryResourceUseNumber());
            desc += formatC1;
        }
        return desc;
    }

    private static String getJoinResourceTypeName(List<ResourceReceiveDetailUsagePO> resourceReceiveDetailUsagePoList, Double maxUtilizationRate) {
        return resourceReceiveDetailUsagePoList.stream().filter(item -> Objects.equals(maxUtilizationRate, item.getFollowedResourceUseRate()))
                .map(ResourceReceiveDetailUsagePO::getResourceTypeName).distinct().collect(Collectors.joining("、"));
    }

    private Map<DateCodeType, List<ResourceReceiveDetailUsagePO>> getResourceReceiveDetailUsagePoList(ReportGenerateParameter parameter) {
        Integer dateCode = parameter.getDateCode();
        DateCodeType codeType = parameter.getCodeType();
        LambdaQueryWrapper<ResourceReceiveDetailUsagePO> queryWrapper = SqlUtil.queryWrapper(parameter,
                w -> w.and(c ->
                        c.or(a -> a.eq(CommonDimensionEntity::getCodeType, codeType).eq(CommonDimensionEntity::getDateCode, dateCode))
                                .or(a -> a.eq(CommonDimensionEntity::getCodeType, DateCodeType.TERM).eq(CommonDimensionEntity::getDateCode, codeType.toTerm(dateCode))))
                ,
                ResourceReceiveDetailUsagePO.class);
        return this.resourceReceiveDetailUsageMapper.selectList(queryWrapper)
                .stream().collect(Collectors.groupingBy(CommonDimensionEntity::getCodeType));
    }

    private Map<DateCodeType, ResourceReceiveTotalUsagePO> getResourceReceiveTotalUsagePo(ReportGenerateParameter parameter) {
        Integer dateCode = parameter.getDateCode();
        DateCodeType codeType = parameter.getCodeType();
        LambdaQueryWrapper<ResourceReceiveTotalUsagePO> queryWrapper = SqlUtil.queryWrapper(parameter,
                w -> w.and(c ->
                        c.or(a -> a.eq(CommonDimensionEntity::getCodeType, codeType).eq(CommonDimensionEntity::getDateCode, dateCode))
                                .or(a -> a.eq(CommonDimensionEntity::getCodeType, DateCodeType.TERM).eq(CommonDimensionEntity::getDateCode, codeType.toTerm(dateCode))))
                ,
                ResourceReceiveTotalUsagePO.class);
        return this.resourceReceiveTotalUsageMapper.selectList(queryWrapper)
                .stream().collect(Collectors.toMap(CommonDimensionEntity::getCodeType, Function.identity(), (t1, t2) -> t1));
    }

}
